package com.hsurosy.hsuchain.nft.limiter;

import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;

/**
 * 滑动窗口限流服务实现类
 *
 * @Author Hsu琛君珩
 * @Date 2024-10-05 4:35
 * @Description
 * @Version: v1.0.0
 */
public class SlidingWindowRateLimiter implements RateLimiter {

    /**
     * Redisson 客户端实例，用于创建限流器
     */
    private RedissonClient redissonClient;

    /**
     * 限流 key 的前缀，避免不同限流规则冲突
     */
    private static final String LIMIT_KEY_PREFIX = "nft:turbo:limit:";

    /**
     * 构造函数，传入 RedissonClient 初始化限流器
     *
     * @param redissonClient Redisson 客户端，用于获取和配置限流器
     */
    public SlidingWindowRateLimiter(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    /**
     * 判断一个 key 是否通过限流规则
     * <p>
     * 使用 Redisson 提供的 RRateLimiter 来设置和检查限流规则。如果该 key 没有被限流器初始化，
     * 会根据给定的限制数和窗口大小创建新的限流器。
     * </p>
     *
     * @param key        限流的 key，通常是某个资源或用户的唯一标识
     * @param limit      限制数量，表示在窗口期内允许的最大请求次数
     * @param windowSize 窗口大小，单位为秒，表示在多少秒的时间窗口内进行限流
     * @return 返回布尔值，true 表示允许通过，false 表示被限制
     */
    @Override
    public Boolean tryAcquire(String key, int limit, int windowSize) {
        // 获取基于 Redisson 的限流器对象
        RRateLimiter rRateLimiter = redissonClient.getRateLimiter(LIMIT_KEY_PREFIX + key);

        // 如果限流器未初始化，则设置限流规则
        if (!rRateLimiter.isExists()) {
            rRateLimiter.trySetRate(RateType.OVERALL, limit, windowSize, RateIntervalUnit.SECONDS);
        }

        // 尝试获取许可，判断当前请求是否可以通过
        return rRateLimiter.tryAcquire();
    }
}
